package com.junyi.basis;

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.junit.jupiter.api.Test;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Date;

/**
 * LocalDate:表示日期，无时区属性
 *
 * LocalTime:表示时间，无时区属性
 *
 * LocalDateTime: LocalDate +LocalTime，无时区属性
 *
 * lnstant:时间戳，类似Date
 *
 * ZonedDateTime:表示日期和时间，有时区属性，类似GregorianCalendar(Calendar的一种)
 *
 * Duration:一段时间
 *
 * Period:一段日期，基于日历系统的日期区间
 *
 * DateTimeFormatter:可以格式化日期时间，线程安全
 *
 * zoneld / ZoneOffset:时区
 *
 * `GMT+08:00`或者`UTC+08:00`表示东八区。`GMT`和`UTC`可以认为基本是等价的，只是`UTC`使用更精确的原子钟计时
 *
 * @time: 2020/10/26 17:54
 * @version: 1.0
 * @author: junyi Xu
 * @description: java 8 time 包下的 API
 * @see java.time.LocalDate
 * @see java.time.LocalDateTime
 */
@Slf4j
public class TimeMy {
    /**
     * 设置格式化模板一。DateTimeFormatter 是线程安全的，所以可以定义为全局静态变量
     **/
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    /** 设置格式化模板二 */
    private static DateTimeFormatter DATE_TIME_FORMATTER2 = new DateTimeFormatterBuilder()
            .appendValue(ChronoField.YEAR)
            .appendLiteral("-")
            .appendValue(ChronoField.MONTH_OF_YEAR)
            .appendLiteral("-")
            .appendValue(ChronoField.DAY_OF_MONTH)
            .appendLiteral(" ")
            .appendValue(ChronoField.HOUR_OF_DAY)
            .appendLiteral(":")
            .appendValue(ChronoField.MINUTE_OF_HOUR)
            .appendLiteral(":")
            .appendValue(ChronoField.SECOND_OF_MINUTE)
            .appendLiteral(".")
            .appendValue(ChronoField.MILLI_OF_SECOND)
            .toFormatter();


    /**
     * 设置日期时区常量
     **/
    public static final ZoneId CHINA_ZONE_ID = ZoneId.systemDefault();


    /**
     * 创建
     */
    @Test
    public void create() {
        LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
        LocalDate d = dt.toLocalDate(); // 转换到当前日期
        LocalTime t = dt.toLocalTime(); // 转换到当前时间

        // 指定日期和时间:
        LocalDate d2 = LocalDate.of(2019, 11, 30); // 2019-11-30, 注意11=11月
        LocalTime t2 = LocalTime.of(15, 16, 17); // 15:16:17
        LocalDateTime dt2 = LocalDateTime.of(2019, 11, 30, 15, 16, 17);
        LocalDateTime dt22 = LocalDateTime.of(d2, t2);      // LocalDate + LocalTime

        LocalDateTime dt3 = LocalDateTime.parse("2019-11-19T15:16:17");
        LocalDate d3 = LocalDate.parse("2019-11-19");
        LocalTime t3 = LocalTime.parse("15:16:17");

        // LocalDateTime 无时区概念，只表示了时间。而 ZonedDateTime = LocalDateTime + Zoned
        ZonedDateTime zonedDateTime = ZonedDateTime.of(dt, ZoneId.of("America/New_York"));
        System.out.println(zonedDateTime);
    }

    /**
     * 日期格式化
     **/
    @Test
    public void formatDate() {
        LocalDateTime now = LocalDateTime.now();
        String s = now.format(DATE_TIME_FORMATTER);
        System.out.println(s);

        String dateStr = "2022-06-15 09:10:50.1234";
        LocalDateTime dateTime = LocalDateTime.parse(dateStr, DATE_TIME_FORMATTER);
        System.out.println(dateTime);
    }

    /**
     * Date LocalDateTime LocalDate 转化
     **/
    @Test
    public void dateToDateTime() {
        // Date -> LocalDateTime
        Date date = new Date();
        LocalDateTime localDateTime = date.toInstant().atZone(CHINA_ZONE_ID).toLocalDateTime();
        System.out.println(localDateTime);

        // LocalDate -> Date
        LocalDate localDate = LocalDate.now();
        Date d1 = Date.from(localDate.atStartOfDay(CHINA_ZONE_ID).toInstant());
        System.out.println(d1);

        // LocalDateTime -> Date
        LocalDateTime localDateTime2 = LocalDateTime.now();
        Date d2 = Date.from(localDateTime2.atZone(CHINA_ZONE_ID).toInstant());
        System.out.println(d2);
    }

    /**
     * 日期加减
     **/
    @Test
    public void plusDay() {
        LocalDateTime dateTime = LocalDateTime.now(CHINA_ZONE_ID);
        //天
        dateTime = dateTime.plusDays(1);
        //时
        dateTime = dateTime.plusHours(-1);
        //分钟
        dateTime = dateTime.plusMinutes(30);
        System.out.println(dateTime.format(DATE_TIME_FORMATTER));

        // 支持链式操作
        LocalDate localDate = LocalDate.now()
                .minus(Period.ofDays(1))
                .minusMonths(1)
                .plus(1, ChronoUnit.DAYS)
                .plus(Period.ofMonths(1));
        System.out.println(localDate);
    }

    /**
     * 日期、时间变更
     **/
    @Test
    public void changeDay() {
        LocalDateTime dt = LocalDateTime.of(2020, 12, 25, 11, 11, 59);
        System.out.println(dt);
        // 日期变为31日:
        LocalDateTime dt2 = dt.withDayOfMonth(31);
        System.out.println(dt2); // 2020-12-31T11:11:59
        // 月份变为3:
        LocalDateTime dt3 = dt2.withMonth(3);
        System.out.println(dt3); // 2020-3-31T11:11:59
    }

    /**
     * 日期时间间隔
     **/
    @Test
    public void betweenDay() {
        // LocalDateTime
        LocalDateTime start = LocalDateTime.of(2019, 07, 01, 12, 12, 22);
        LocalDateTime end = LocalDateTime.of(2020, 07, 27, 12, 12, 22);
        Long withSecond = end.atZone(CHINA_ZONE_ID).toEpochSecond() - start.atZone(CHINA_ZONE_ID).toEpochSecond();
        System.out.println(withSecond / 60 / 60 / 24);    // 两个时间相差392天

        // LocalDate
        LocalDate startDate2 = LocalDate.of(2019, 07, 01);
        LocalDate endDate2 = LocalDate.of(2019, 07, 03);
        Long withDay = endDate2.toEpochDay() - startDate2.toEpochDay();
        System.out.println(withDay);        // 2

        // 更简单的做法
        long day = ChronoUnit.DAYS.between(startDate2, endDate2);       // 2
        System.out.println(day);

        // Duration 和 Period 的表示方法符合ISO 8601的格式，它以P...T...的形式表示，P...T之间表示日期间隔，T后面表示时间间隔。如果是PT...的格式表示仅有时间间隔。
        start = LocalDateTime.of(2020, 12, 25, 11, 11, 11);
        end = LocalDateTime.of(2020, 12, 29, 12, 12, 12);
        Duration d = Duration.between(start, end);
        System.out.println(d); // PT97H1M1S，表示相差了97小时1分钟1秒

        Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
        System.out.println(p); // P1M21D，表示相差了1月21天

        Duration d2 = Duration.parse("P1DT2H3M"); // 1 day, 2 hours, 3 minutes
    }

    /**
     * 获取特定的时间
     **/
    @Test
    public void theLastDay() {
        // 当月第一天
        LocalDateTime dateTime = LocalDateTime.of(2019, 07, 03, 12, 12, 22);
        dateTime = dateTime.with(TemporalAdjusters.firstDayOfMonth());
        System.out.println(dateTime);
        // 当月最后一天
        dateTime = dateTime.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println(dateTime);
        // 当月第一个周一
        dateTime = dateTime.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
        System.out.println(dateTime);
        // 今天之前的一个周六
        dateTime = dateTime.with(TemporalAdjusters.previous(DayOfWeek.SATURDAY));
        System.out.println(dateTime);
        // 本月最后一个工作日
        dateTime = dateTime.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY));
        System.out.println(dateTime);

        //当月的第几天
        dateTime = LocalDateTime.now();
        int dayOfMonth = dateTime.getDayOfMonth();
        System.out.println(dayOfMonth);
        // 当前周的第几天
        int dayOfWeek = dateTime.getDayOfWeek().getValue();
        System.out.println(dayOfWeek);
    }

    /** 时区相关 */
    @Test
    public void zone() {
        String stringDate = "2020-01-02 22:00:00.123";
        ZoneId timeZoneSH = ZoneId.of("Asia/Shanghai");
        ZoneId timeZoneNY = ZoneId.of("America/New_York");
        ZoneId timeZoneJST = ZoneOffset.ofHours(9);

        ZonedDateTime date = ZonedDateTime.of(LocalDateTime.parse(stringDate, DATE_TIME_FORMATTER), timeZoneJST);
        System.out.println(date);

        System.out.println(timeZoneSH.getId() + ": " + DATE_TIME_FORMATTER.withZone(timeZoneSH).format(date));
        System.out.println(timeZoneNY.getId() + ": " + DATE_TIME_FORMATTER.withZone(timeZoneNY).format(date));
        System.out.println(timeZoneJST.getId() + ": " + DATE_TIME_FORMATTER.withZone(timeZoneJST).format(date));
    }

    /**
     * 获取当前时间的时间戳，单位为毫秒
     */
    @Test
    public void currentTimeMillis() {
        long current = System.currentTimeMillis();
        log.info(current + "");
    }

    /**
     * Calendar——老的类，建议使用新的时间类 LocalDateTime
     */
    @Test
    public void Calendar() {
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        // 增加30天
        c.add(Calendar.DAY_OF_MONTH, 30);
        System.out.println(c.getTime());
    }


}
